#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  if (this->operateur != "0") {
        int og=operandeGauche->getValeur();
        int od=operandeDroit->getValeur();
         if      (this->operateur=="+") valeur = og+od; 
         else if (this->operateur=="-") valeur = og-od;
         else if (this->operateur=="*") valeur = og*od; 
         else if (this->operateur=="/")
                if (od!=0)
                        valeur = og/od;
                else {
                        cout << "Erreur pendant l'interpretation : division par zero" << endl;
                        exit(0); //plus tard on levera une exception
                }
         else if (this->operateur=="==")
             valeur = (og == od);
         else if (this->operateur=="!=")
             valeur = (og != od);
         else if (this->operateur=="<")
             valeur = (og < od);
         else if (this->operateur=="<=")
             valeur = (og <= od);
         else if (this->operateur==">")
             valeur = (og > od);
         else if (this->operateur==">=")
             valeur = (og >= od);
          else // this->operateur=="non"
              valeur = (od ? 0 : 1);
  } else
      valeur = 1;
 
  return valeur;
  //== | != | < | <= | > | >=
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  if (operandeGauche)
        operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  if (operandeDroit)
        operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}


////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Si " << endl;
    expBool->afficher(indentation+1);
    seqInst->afficher(indentation+1);
    if (sinon)
        sinon->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
    if (expBool->getValeur())
        return seqInst->getValeur();
    else
    {
        if (sinon)
            return sinon->getValeur();
        else
            return (new NoeudSeqInst())->getValeur();
    }
}

////////////////////////////////////////////////////////////////////////////////
NoeudInstSi::NoeudInstSi(Noeud* expBool, Noeud* seqInst, NoeudInstSi* sinon) {
    this->expBool = expBool;
    this->seqInst = seqInst;
    this->sinon = sinon;    
}
////////////////////////////////////////////////////////////////////////////////

NoeudInstLoop::NoeudInstLoop(Noeud* expBool, Noeud* seqInst){
    this->expBool = expBool;
    this->seqInst = seqInst;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstLoop::afficher(unsigned short indentation,string message){
    Noeud::afficher(indentation);
    cout << message << endl;
    expBool->afficher(indentation+1);
    seqInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
NoeudInstTantque::NoeudInstTantque(Noeud* expBool, Noeud* seqInst) 
: NoeudInstLoop(expBool,seqInst){
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTantque::afficher(unsigned short indentation) {
    NoeudInstLoop::afficher(indentation,"Noeud - Instruction Tant que");
}    
////////////////////////////////////////////////////////////////////////////////

int NoeudInstTantque::getValeur() {
    int valeur= 0;
    while (getCond()->getValeur())
        valeur = getSeq()->getValeur();
    
    return valeur;    
}
////////////////////////////////////////////////////////////////////////////////
NoeudInstRepeter::NoeudInstRepeter(Noeud* expBool, Noeud* seqInst) 
: NoeudInstLoop(expBool,seqInst){
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation) {
    NoeudInstLoop::afficher(indentation,"Noeud - Instruction Repeter");
}    
////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    int valeur= 0;
    do
        valeur = getSeq()->getValeur();
    while (!getCond()->getValeur());
    return valeur;    
}
////////////////////////////////////////////////////////////////////////////////
NoeudInstPour::NoeudInstPour(Noeud* expBool, Noeud* seqInst,Noeud* init, Noeud* iter) 
: NoeudInstLoop(expBool,seqInst){
    this->init = init;
    this->iter = iter;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
    NoeudInstLoop::afficher(indentation,"Noeud - Instruction Pour");
    init->afficher(indentation+1);
    iter->afficher(indentation+1);
}    
////////////////////////////////////////////////////////////////////////////////

int NoeudInstPour::getValeur() {
    int valeur= 0;
    init->getValeur();
    while(getCond()->getValeur())
    {
        getSeq()->getValeur();
        iter->getValeur();
    }
    return valeur;    
}
////////////////////////////////////////////////////////////////////////////////
NoeudInstLire::NoeudInstLire(Noeud* var) {
    this->var=var;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout <<"Noeud - Instruction Lire " << endl;
    var->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
    int valeur;
    cin >> valeur;
    ((SymboleValue*)var)->setValeur(valeur);
    return var->getValeur();
} 
////////////////////////////////////////////////////////////////////////////////
NoeudInstEcrire::NoeudInstEcrire(Noeud* var) {
    this->var=var;    
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout <<"Noeud - Instruction Ecrire " << endl;
    var->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
    cout << var->getValeur() << endl;
    return 0;
}